<?php

namespace app\common\service;


use app\common\exception\ApiException;
use app\common\lib\aes\WXBizDataCrypt;
use think\Cache;

class UserToken extends BaseToken {
    protected $wxAppID;
    protected $wxAppSecret;
    protected $wxLoginUrl;
    protected $signature;
    protected $encryptedData;
    protected $iv;
    protected $accessTokenUrl;

    public function __construct() {
        $this->wxAppID     = config('wx.app_id');
        $this->wxAppSecret = config('wx.app_secret');
    }

    public function getToken($code) {
        $this->wxLoginUrl = sprintf(config('wx.login_url'), $this->wxAppID, $this->wxAppSecret, $code);
        $result           = curl_get($this->wxLoginUrl);
        $wxResult         = json_decode($result, true);
        if (empty($wxResult)) {
            throw new ApiException('获取session_key及openID时异常，微信内部错误', 400);
        } else {
            $loginFail = array_key_exists('errcode', $wxResult);
            if ($loginFail) {
                throw new ApiException($wxResult['errmsg'], 400);
            } else {
                return $this->grantToken($wxResult);
            }
        }
    }

    public function putUserInfo($data) {
        $tokenKey   = request()->header('token');
        $tokenValue = Cache::get($tokenKey);
        if (!$tokenValue) {
            throw new ApiException('滚回去登录', 200, 10);
        }
        $this->signature     = $data['signature'];
        $this->encryptedData = $data['encryptedData'];
        $this->iv            = $data['iv'];
        if (!is_array($tokenValue)) {
            $tokenValue = json_decode($tokenValue, true);
        }
        $this->checkSignature($data['rawData'], $tokenValue['session_key']);
        $userData = json_decode($this->decryptedData($tokenValue['session_key']), true);
        $id       = model('User')->saveUser($userData);
        if (!$id) {
            throw new ApiException('保存用户信息失败', 200, 10);
        }
        return $id;
    }

    public function getAccessToken() {
        $accessToken = Cache::get('access_token');
        if ($accessToken) {
            if (!is_array($accessToken)) {
                $accessToken = json_decode($accessToken, true);
            }
            return $accessToken['access_token'];
        }
        $this->accessTokenUrl = sprintf(config('wx.access_token_url'), $this->wxAppID, $this->wxAppSecret);
        $wxResult             = curl_get($this->accessTokenUrl);
        if (!$wxResult) {
            throw new ApiException('获取access_token错误', 400, 1);
        }
        $wxResult = json_decode($wxResult, true);
        if (array_key_exists('errcode', $wxResult)) {
            throw new ApiException($wxResult['errmsg'], 400, 1);
        }
        $result = Cache::set('access_token', $wxResult, $wxResult['expires_in']);
        if (!$result) {
            throw new ApiException('保存access_token错误', 400, 1);
        }
        return $wxResult['access_token'];
    }

    private function grantToken($wxResult) {
        $id = model('User')->saveUser($wxResult);
        model('User')->where(['id' => $id])->setInc('login_count');
        $cachedValue = $this->prepareCachedValue($wxResult, $id);
        $token       = $this->saveToCache($cachedValue);
        return $token;
    }

    private function prepareCachedValue($wxResult, $id) {
        $cachedValue            = $wxResult;
        $cachedValue['user_id'] = $id;
        return $cachedValue;
    }

    private function decryptedData($session_key) {
        $pc      = new WXBizDataCrypt($this->wxAppID, $session_key);
        $errCode = $pc->decryptData($this->encryptedData, $this->iv, $data);
        if ($errCode == 0) {
            return $data;
        } else {
            throw new ApiException('解码失败', 400);
        }
    }

    private function checkSignature($rowData, $session_key) {
        $signature2 = sha1($rowData . $session_key);
        if ($this->signature === $signature2) {
            return true;
        } else {
            throw new ApiException('非法数据', 403);
        }
    }
}